CS = (typeof CS != "undefined")?  CS : {};


CS.RenderManager = function(engine) 
{ 
	this.Engine = engine;
	this.shaderProgram = null;
	this.currentRotationX = 0.0;
	this.currentRotationY = 0.0;
	this.viewDistance = 0.0;
	this.scale = 1;

	this.pMatrix = null; 
	this.mvMatrix = null;
	this.mvMatrixStack = [];
	this.matrixpool = [];

	this.InitShaders = function () {
		var gl = this.Engine.gl;
		var fragmentShader = this.Engine.Loader.LoadShader("shader-fs");
		var vertexShader = this.Engine.Loader.LoadShader("shader-vs");

		this.shaderProgram = gl.createProgram();
		gl.attachShader(this.shaderProgram, vertexShader);
		gl.attachShader(this.shaderProgram, fragmentShader);
		gl.linkProgram(this.shaderProgram);

		if (!gl.getProgramParameter(this.shaderProgram, gl.LINK_STATUS)) {
			CS.Log.error("InitShaders: Could not initialise shaders");
			return false;
		}

		gl.useProgram(this.shaderProgram);

		this.shaderProgram.vertexPositionAttribute = gl.getAttribLocation(this.shaderProgram, "aVertexPosition");
		gl.enableVertexAttribArray(this.shaderProgram.vertexPositionAttribute);

		this.shaderProgram.normalsAttribute = gl.getAttribLocation(this.shaderProgram, "aNormals");
		gl.enableVertexAttribArray(this.shaderProgram.normalsAttribute);

		this.shaderProgram.textureCoordAttribute = gl.getAttribLocation(this.shaderProgram, "aTextureCoord");

		this.shaderProgram.pMatrixUniform = gl.getUniformLocation(this.shaderProgram, "uPMatrix");
		this.shaderProgram.mvMatrixUniform = gl.getUniformLocation(this.shaderProgram, "uMVMatrix");
		this.shaderProgram.nMatrixUniform = gl.getUniformLocation(this.shaderProgram, "uNMatrix");
		this.shaderProgram.samplerUniform = gl.getUniformLocation(this.shaderProgram, "uSampler");
		this.shaderProgram.useTexturesUniform = gl.getUniformLocation(this.shaderProgram, "uUseTextures");
		this.shaderProgram.ambientLight = gl.getUniformLocation(this.shaderProgram, "uAmbientLight");
		this.shaderProgram.directionnalLight = gl.getUniformLocation(this.shaderProgram, "uDirectionnalLight");

		return true;
	};
  
	this.init = false;

	this.InitCamera = function () {
		this.init = true;
		var gl = this.Engine.gl;
		gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);

		this.perspective(45.0, gl.viewportWidth / gl.viewportHeight, 0.001, 100.0);
		this.loadIdentity();
		
		var maxdim = -Infinity;
		var midx = 0;
		var midy = 0;
		var midz = 0;
		var objects = this.Engine.objects;
		for (obj in objects) {
			if(objects[obj].factory) {

				var bbox = objects[obj].factory.bbox;

				midx += (bbox.min.x + bbox.max.x) / 2;
				midy += (bbox.min.y + bbox.max.y) / 2;
				midz += (bbox.min.z + bbox.max.z) / 2;

				maxdim = Math.max(maxdim, bbox.max.x - bbox.min.x);
				maxdim = Math.max(maxdim, bbox.max.y - bbox.min.y);
				maxdim = Math.max(maxdim, bbox.max.z - bbox.min.z);
			}  
		}

		var eyelook = [midx, midy, midz+(maxdim*1.25)];
		var centerlook = [midx, midy, midz];
		this.mvMatrix = mat4.lookAt(eyelook, centerlook, [0,1,0]);
	};
  
	this.SetMatrixUniforms = function() {
		var gl = this.Engine.gl;
		gl.uniformMatrix4fv(this.shaderProgram.pMatrixUniform, false, this.pMatrix);
		gl.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.mvMatrix);
		
		var normalMatrix = mat4.inverse(this.mvMatrix, this.createMatrix());
		mat4.transpose(normalMatrix);
		gl.uniformMatrix4fv(this.shaderProgram.nMatrixUniform, false, normalMatrix);
		this.freeMatrix(normalMatrix);
	}; 

	this.Frame = function() {
		var gl = this.Engine.gl;
		if (!this.init) return;
		
		//Clear the canvas
		gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		
		this.mvPushMatrix();
		this.mvRotate(this.currentRotationX,[0,1,0]);
		this.mvRotate(this.currentRotationY,[1,0,0]);
		//this.mvTranslate([0.0, 0.0, this.viewDistance]);
		this.mvScale([this.scale, this.scale, this.scale]);
		
		gl.uniform3f(this.shaderProgram.ambientLight,0.8,0.8,0.8);
		gl.uniform3f(this.shaderProgram.directionnalLight,0.8,0.8,0.8);
		var objects = this.Engine.objects;
		for (obj in objects) {
			if(objects[obj].factory) {
				this.mvPushMatrix();
				var buffers = objects[obj].factory.glbuffers;

				gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
				gl.vertexAttribPointer(this.shaderProgram.vertexPositionAttribute, buffers.position.itemSize, gl.FLOAT, false, 0, 0);

				gl.bindBuffer(gl.ARRAY_BUFFER, buffers.normal);
				gl.vertexAttribPointer(this.shaderProgram.normalsAttribute, buffers.normal.itemSize, gl.FLOAT, false, 0, 0);


				if (buffers['texcoord']) {
					gl.bindBuffer(gl.ARRAY_BUFFER, buffers['texcoord']);
					gl.enableVertexAttribArray(this.shaderProgram.textureCoordAttribute);
					gl.vertexAttribPointer(this.shaderProgram.textureCoordAttribute,  buffers['texcoord'].itemSize, gl.FLOAT, false, 0, 0);
				} else {
					gl.disableVertexAttribArray(this.shaderProgram.textureCoordAttribute);
				}      

				this.SetMatrixUniforms();

				var submeshes = objects[obj].factory.submeshes;
				for (s in submeshes) {
					var sub = submeshes[s];
					if (!sub.glbuffers.indexbuffer) {
					CS.Log.error("Frame: submeshes: no indexbuffer! "+s);
					continue;
					}
					gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sub.glbuffers.indexbuffer);
					gl.drawElements(gl.TRIANGLES, sub.glbuffers.indexbuffer.numItems, gl.UNSIGNED_SHORT, 0);

					for (m in sub.shadervars) {
						var v = sub.shadervars[m];
						if (v.type == 'texture' && v.value.loaded) {
							gl.activeTexture(gl.TEXTURE0);
							gl.bindTexture(gl.TEXTURE_2D, v.value);
							gl.uniform1i(this.shaderProgram.samplerUniform, 0);   
						}
					}
				}

				this.mvPopMatrix();
			}
		}
		this.mvPopMatrix();
	};

	this.mvPushMatrix = function(m) {
		if(m){
			if (this.mvMatrix) this.freeMatrix(this.mvMatrix);
			this.mvMatrix = this.createMatrix(m);
			this.mvMatrixStack.push(this.mvMatrix);

		}else
			this.mvMatrixStack.push(this.createMatrix(this.mvMatrix));
	};

	this.mvPopMatrix = function() {
		if (this.mvMatrixStack.length == 0) throw "Invalid popMatrix!";
		if (this.mvMatrix) this.freeMatrix(this.mvMatrix);
		this.mvMatrix = this.mvMatrixStack.pop();
		return this.mvMatrix;
	}

	this.loadIdentity = function() {
		if (!this.mvMatrix) this.mvMatrix = this.createMatrix();
		mat4.identity(this.mvMatrix);
	}

	this.mvTranslate = function(v) {
		mat4.translate(this.mvMatrix, v);
	}

	this.mvRotate = function(ang, v) {
		var arad = ang * Math.PI / 180.0;
		mat4.rotate(this.mvMatrix, arad, v);
	}
	
	this.mvScale = function (v) {
		mat4.scale(this.mvMatrix, v);
	}

	this.perspective = function(fovy, aspect, znear, zfar) {
		this.pMatrix = mat4.perspective(fovy, aspect, znear, zfar);
	}
	
	this.createMatrix = function(m) {
		var dest;
		if (this.matrixpool.length == 0) {
			return mat4.create(m);
		}
		dest = this.matrixpool.pop();
		if (m) {
			dest[0] = m[0];
			dest[1] = m[1];
			dest[2] = m[2];
			dest[3] = m[3];
			dest[4] = m[4];
			dest[5] = m[5];
			dest[6] = m[6];
			dest[7] = m[7];
			dest[8] = m[8];
			dest[9] = m[9];
			dest[10] = m[10];
			dest[11] = m[11];
			dest[12] = m[12];
			dest[13] = m[13];
			dest[14] = m[14];
			dest[15] = m[15];
		}
		return dest;
	}

	this.freeMatrix = function(m) {
		this.matrixpool.push(m);
	}

};
